/***************************************************************************************

***************************************************************************************/

/*
*************************************************************************************
* File name: header.c
* Function: IVC Slice headers
*
*************************************************************************************
*/


#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#include "memalloc.h"
#include "global.h"
#include "elements.h"
#include "defines.h"
#include "vlc.h"
#include "header.h"
#include "AEC.h"  
#include "math.h"
#include "checksyntax.h"
CameraParamters CameraParameter, *camera=&CameraParameter;

extern StatBits *StatBitsPtr;  //ITM_r2

const int  InterlaceSetting[] = { 4, 4, 0, 5, 0, 5, 0, 5, 3, 3, 3, 3, 0, 
0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 0, 0, 4, 0};  //ITM
/*
*************************************************************************
* Function:sequence header  
* Input:
* Output:
* Return: 
* Attention:
*************************************************************************
*/

void SequenceHeader (unsigned char *buf,int startcodepos, int length)
{

	memcpy (currStream->streamBuffer, buf, length);
	currStream->code_len = currStream->bitstream_length = length;
	currStream->read_len = currStream->frame_bitoffset = (startcodepos+1)*8;

	profile_id                  = u_v  (8, "profile_id"  );
	level_id                    = u_v  (8, "level_id"    );
	progressive_sequence        = 1;
	horizontal_size             = u_v  (14, "horizontal_size"  );
	vertical_size               = u_v  (14, "vertical_size"  );
	chroma_format               = u_v  (2, "chroma_format"  );
	sample_precision            = u_v  (3, "sample_precision"  );
	aspect_ratio_information    = u_v  (4, "aspect_ratio_information"  );
	frame_rate_code             = u_v  (4, "frame_rate_code"  );

	bit_rate_lower              = u_v  (18, "bit_rate_lower"  );
	marker_bit                  = u_v  (1,  "marker bit"  );          
	sprintf(spos0, "Sequence_header");                                
	sprintf(spos1, "bit_rate_lower");                                 
	//CHECKMARKERBIT                                                  
	bit_rate_upper              = u_v  (12, "bit_rate_upper"  );
	low_delay                   = u_v  (1, "low_delay"  );
	marker_bit                  = u_v  (1, "marker bit"  );           
	sprintf(spos0, "Sequence_header");                                
	sprintf(spos1, "low_delay");                                      
	//CHECKMARKERBIT                                                  
	bbv_buffer_size = u_v(18,"bbv buffer size");
    abt_enable = u_v(1, "abt enable");
    if_type    = u_v(1, "if type");
	u_v  (4,"reseved bits"  );

	img->max_ref_num = 32;

	img->width          = horizontal_size;
	img->height         = vertical_size;
	img->width_cr       = (img->width>>1);
    img->height_cr      = (img->height>>1);
	img->iStride        = img->width + 2*IMG_PAD_SIZE;
	img->iStrideC       = img->iStride >> 1;
	img->PicWidthInMbs  = img->width/MB_SIZE;
	img->PicHeightInMbs = img->height/MB_SIZE;
	img->PicSizeInMbs   = img->PicWidthInMbs * img->PicHeightInMbs;
	img->buf_cycle      = input->buf_cycle+1;
	img->max_mb_nr      =(img->width * img->height) / (MB_SIZE * MB_SIZE);
	//assert(img->PicSizeInMbs == img->max_mb_nr);
#if M38817_DATA_STUFFING
	IsIVCStuffingPattern(0, currStream->bitstream_length*8 - currStream->frame_bitoffset);
#else
	IsStuffingPattern(0, currStream->bitstream_length*8 - currStream->frame_bitoffset);  //ITM
#endif
	CheckHighLevelSyntax(0);	//ITM_r2
}


void video_edit_code_data(char *buf,int startcodepos, int length)
{
  currStream->frame_bitoffset = currStream->read_len = (startcodepos+1)*8;
  currStream->code_len = currStream->bitstream_length = length;
  memcpy (currStream->streamBuffer, buf, length);
  vec_flag = 1;
}
/*
*************************************************************************
* Function:I picture header  //sw
* Input:
* Output:
* Return: 
* Attention:
*************************************************************************
*/

void I_Picture_Header(char *buf,int startcodepos, int length)
{
	currStream->frame_bitoffset = currStream->read_len = (startcodepos+1)*8;
	currStream->code_len = currStream->bitstream_length = length;
	memcpy (currStream->streamBuffer, buf, length);

	bbv_delay = u_v(16,"bbv_delay");


	time_code_flag       = u_v(1,"time_code_flag");
	if (time_code_flag)
		time_code        = u_v(24,"time_code");

	marker_bit           = u_v(1,"marker_bit");
	sprintf(spos0, "I_Picture_Header");                                 
	sprintf(spos1, "time_code_flag");                                   
	//CHECKMARKERBIT                                                    
	picture_distance         = u_v(8,"picture_distance");

	//xyji 12.23 
	if(low_delay)
	{
		bbv_check_times = ue_v("bbv check times");
	}

	progressive_frame    = 1;

	fixed_picture_qp     = u_v(1,"fixed_picture_qp");
	picture_qp           = u_v(6,"picture_qp");

	u_v(4,"reserved bits");		

	loop_filter_disable = u_v(1,"loop_filter_disable");
	if (!loop_filter_disable)
	{
		Alpha =u_v(8,"alpha");
		Beta =u_v(6,"beta");
	}

	img->qp                = picture_qp;
	img->pic_distance      = picture_distance;	
	img->type              = I_IMG;

	fprintf(p_sreport, "\n*********** Pic: %d (I/P) Type %s **********\n", picture_distance, "INTRA");  
#if M38817_DATA_STUFFING
	IsIVCStuffingPattern(0, currStream->bitstream_length*8 - currStream->frame_bitoffset);
#else
	IsStuffingPattern(0, currStream->bitstream_length*8 - currStream->frame_bitoffset);    
#endif
	CheckHighLevelSyntax(1);	//ITM_r2
	//ITM
}

/*
*************************************************************************
* Function:pb picture header 
* Input:
* Output:
* Return: 
* Attention:
*************************************************************************
*/
void PB_Picture_Header(char *buf,int startcodepos, int length)
{
	currStream->frame_bitoffset = currStream->read_len = (startcodepos+1)*8;
	currStream->code_len = currStream->bitstream_length = length;
	memcpy (currStream->streamBuffer, buf, length);

	bbv_delay = u_v(16,"bbv delay");

	picture_coding_type       = u_v(2,"picture_coding_type");
	img->p_subtype = u_v(2, "P picture subtype");

	picture_distance         = u_v(8,"picture_distance");

	if(low_delay)
	{
		bbv_check_times = ue_v("bbv check times");
	}

	progressive_frame =1;

	fixed_picture_qp       = u_v(1,"fixed_picture_qp");
	picture_qp             = u_v(6,"picture_qp");

	img->no_forward_reference = u_v(1, "no_forward_reference_flag");    
	u_v(3,"reserved bits");	

	loop_filter_disable = u_v(1,"loop_filter_disable");
	if (!loop_filter_disable)
	{
		Alpha =u_v(8,"alpha");
		Beta =u_v(6,"beta");
	}
	img->qp                = picture_qp;

	if(picture_coding_type==1) {
		img->type = P_IMG;
	} else {
		img->type = B_IMG;
	}

	img->pic_distance      = picture_distance;	

	fprintf(p_sreport, "\n*********** Pic: %d (I/P) Type %s **********\n", picture_distance, 
		img->type==P_IMG?"P_IMG":"B_IMG");  
#if M38817_DATA_STUFFING
	IsIVCStuffingPattern(0, currStream->bitstream_length*8 - currStream->frame_bitoffset);
#else
	IsStuffingPattern(0, currStream->bitstream_length*8 - currStream->frame_bitoffset);   
#endif
	CheckHighLevelSyntax(2);	//ITM_r2
	if (img->no_forward_reference) {
		CSyntax = 0;
		fprintf(p_sreport, "no_forward_reference_flag should be set to 0 in P frame!\n");	
	}
}

/*
*************************************************************************
* Function:user data 
* Input:
* Output:
* Return: 
* Attention:
*************************************************************************
*/

void user_data(char *buf,int startcodepos, int length)
{
  int user_data;
  int i;

  memcpy (currStream->streamBuffer, buf, length);

  currStream->code_len = currStream->bitstream_length = length;
  currStream->read_len = currStream->frame_bitoffset = (startcodepos+1)*8;

  for(i=0; i<length-4; i++)
    user_data = u_v (8, "user data");
}

/*
*************************************************************************
* Function:To calculate the poc values
* Input:
* Output:
* Return: 
* Attention:
*************************************************************************
*/

void calc_picture_distance(img_params *img)
{
  static int flag = 0;
  unsigned int        MaxPicDistanceLsb = (1<<8);
  flag ++;
  // for POC mode 1:

  // Calculate the MSBs of current picture
  if( img->pic_distance  <  img->PrevPicDistanceLsb  &&  
    ( img->PrevPicDistanceLsb - img->pic_distance )  >=  ( MaxPicDistanceLsb / 2 ) )
    img->CurrPicDistanceMsb = img->PicDistanceMsb + MaxPicDistanceLsb;
  else if ( img->pic_distance  >  img->PrevPicDistanceLsb  &&
    ( img->pic_distance - img->PrevPicDistanceLsb )  >  ( MaxPicDistanceLsb / 2 ) )
    img->CurrPicDistanceMsb = img->PicDistanceMsb - MaxPicDistanceLsb;
  else
    img->CurrPicDistanceMsb = img->PicDistanceMsb;


    img->ThisPOC = img->framepoc;

  img->tr_frm = img->ThisPOC/2;
  img->tr = img->ThisPOC;
  //moved from above for stuff that still uses img->tr

  if(img->type != B_IMG) 
  {
    img->imgtr_last_prev_P = img->imgtr_last_P;
    img->imgtr_last_P = img->imgtr_next_P;
    img->imgtr_next_P = picture_distance; 
    img->Bframe_number=0;
  }
  if(img->type==B_IMG)
  {
    img->Bframe_number++;
  }

}

/*
*************************************************************************
* Function:slice header  
* Input:
* Output:
* Return: 
* Attention:         
*************************************************************************
*/
void SliceHeader (char *buf,int startcodepos, int length)
{
	int mb_row;		
	int mb_index;	
	int mb_width, mb_height;	

	memcpy (currStream->streamBuffer, buf, length);
	currStream->code_len = currStream->bitstream_length = length;

	currStream->read_len = currStream->frame_bitoffset = (startcodepos)*8; 
	slice_vertical_position              = u_v  (8, "slice vertical position"); 

	if(vertical_size > 2800)              
		slice_vertical_position_extension  = u_v  (3, "slice vertical position extension");

	if (vertical_size > 2800) 
		mb_row = (slice_vertical_position_extension << 7) + slice_vertical_position;
	else
		mb_row = slice_vertical_position;

	mb_width  = (horizontal_size + 15) / 16;
	mb_height = (vertical_size + 15) / 16;

	mb_index = mb_row * mb_width;


	if (!fixed_picture_qp)
	{
		fixed_slice_qp   = u_v  (1,"fixed_slice_qp");
		slice_qp         = u_v  (6,"slice_qp");

		img->qp=slice_qp;
	}

	CheckHighLevelSyntax(7);		  					 

}

/*
*************************************************************************
* Function:Error handling procedure. Print error message to stderr and exit
with supplied code.
* Input:text
Error message
* Output:
* Return: 
* Attention:
*************************************************************************
*/

void error(char *text, int code)
{
  fprintf(stderr, "%s\n", text);
  exit(code);
}

//Lou
int sign(int a , int b)
{
  int x;

  x=abs(a);

  if (b>0)
    return(x);
  else 
    return(-x);
}

void CheckHighLevelSyntax(int idx)
{
  //0: sequence_header     
  //1: i_picture_header    
  //2: pb_picture_header
  //7: slice
  int   DropFrameFlag, TimeCodeHours, TimeCodeMinutes, TimeCodeSeconds, TimeCodePictures;
  int   curr_mb_row;

  switch(idx) {
  case 0:
    if(profile_id != 0x20)
    {
      CSyntax = 0;
      fprintf(p_sreport, "\nInvalid Profile_ID: %d!\n", profile_id);
    }

    if(level_id!=0x10&&level_id!=0x20&&level_id!=0x22&&level_id!=0x40&&level_id!=0x42)
    {
      CSyntax = 0;
      fprintf(p_sreport, "\nInvalid Level_ID: %d!\n", level_id);
    }

    if(sample_precision==0)
    {
      CSyntax = 0;
      fprintf(p_sreport, "\nsample_precision %d is forbidden at the current profile!\n", sample_precision);
    }
    else if(sample_precision!=1)
    {
      CSyntax = 0;
      fprintf(p_sreport, "\nsample_precision %d is reserved at the current profile!\n", sample_precision);
    }

    if(aspect_ratio_information==0)
    {
      CSyntax = 0;
      fprintf(p_sreport, "\aspect_ratio %d is forbidden at the current profile!\n", sample_precision);
    }
    else if(aspect_ratio_information>4)
    {
      CSyntax = 0;
      fprintf(p_sreport, "\aspect_ratio %d is reserved at the current profile!\n", sample_precision);
    }

    if(frame_rate_code<1||frame_rate_code>8)
    {
      CSyntax = 0;
      fprintf(p_sreport, "Undefined frame_rate %d in all levels. \n", frame_rate_code);
    }

    switch (level_id)
    {
    case 0x10:
    case 0x11:
      {
        if(horizontal_size>352)
        {
          CSyntax = 0;
          fprintf(p_sreport, "Image Width:%d exceeds level 2.0 restriction.\n",horizontal_size);
        }
        else 
          if(vertical_size>288)
          {
            CSyntax = 0;
            fprintf(p_sreport, "Image height:%d exceeds level 2.0 and level 2.1's restriction.\n",vertical_size);
          }
          if((long)(horizontal_size)*(long)(vertical_size)/256>396)
          {
            CSyntax = 0;
            fprintf(p_sreport, "The number of macroblock per frame is:%i, which exceeds 396 in level 2.0.\n",
              (long)((long)(horizontal_size)*(long)(vertical_size)/256));
          }

          if(chroma_format!=1)
          {
            CSyntax = 0;
            fprintf(p_sreport, "Chroma_format is %d. In level 2.0 only format 4:2:0 is supported.\n",chroma_format);
          }

          if(level_id==0x10 && bbv_buffer_size>122880)
          {
            CSyntax = 0;
            fprintf(p_sreport, "Current Bbv_Buffer_Size is %d. Invalid Bbv_Buffer_Size input!\n",bbv_buffer_size);
          }

          if(level_id==0x11 && bbv_buffer_size>311296)
          {
            CSyntax = 0;
            fprintf(p_sreport, "Current Bbv_Buffer_Size is %d. Invalid Bbv_Buffer_Size input!\n",bbv_buffer_size);
          }
          break;  
      }

    case 0x20:
      {
        if(horizontal_size>720)
        {
          CSyntax = 0;
          fprintf(p_sreport, "Image Width:%d exceeds level 4.0's restriction.\n",horizontal_size);
        }
        else 
          if(vertical_size>576)
          {
            CSyntax = 0;
            fprintf(p_sreport, "Imgage Height:%d exceeds level 4.0's restriction.\n",vertical_size);
          }

          if((long)(horizontal_size)*(long)(vertical_size)/256>4050)
          {
            CSyntax = 0;
            fprintf(p_sreport, " \n The number of macroblock per frame is %i, which exceeds 1,620 for level 4.0.\n",
              (long)((long)(horizontal_size)*(long)(vertical_size)/256));
          }
          if(chroma_format!=1)
          {
            CSyntax = 0;
            fprintf(p_sreport, "Current chroma_format is %d. In level 4.0 only format 4:2:0 is supported.\n",chroma_format);
          }

          if(bbv_buffer_size>1228800) 
          {
            CSyntax = 0;
            fprintf(p_sreport, "Current Bbv_Buffer_Size is %d. Invalid Bbv_Buffer_Size input.\n",bbv_buffer_size);
          }

          break;
      }
    case 0x40:
    case 0x41:  
      {
        if(horizontal_size>1920)
        {
          CSyntax = 0;
          fprintf(p_sreport, "Image Width:%d exceeds level 6.0 and level 6.0.1's restriction.\n",horizontal_size);
        }
        else 
          if(vertical_size>1152)
          {
            CSyntax = 0;
            fprintf(p_sreport, "Image Height:%d exceeds level 6.0 and level 6.0.1's restriction.\n",vertical_size);
          }
          if((long)(horizontal_size)*(long)(vertical_size)/256>8160)
          {
            CSyntax = 0;
            fprintf(p_sreport, " \n The number of macroblock per frame is %i, which exceeds 8160 for level 6.0 and level 6.0.1.\n",
              (long)((long)(horizontal_size)*(long)(vertical_size)/256));
          }
          if(chroma_format!=1)
          {
            CSyntax = 0;
            fprintf(p_sreport, "Current chroma_format is %d. In level 6.0 and level 6.0.1 only format 4:2:0 is supported.\n",chroma_format);
          }

          if(level_id==0x40 && bbv_buffer_size>2457600) 
          {
            CSyntax = 0;
            fprintf(p_sreport, "Current Bbv_Buffer_Size is %d. Invalid Bbv_Buffer_Size input.\n",bbv_buffer_size);
          }

          if(level_id==0x41 && bbv_buffer_size>6144000) 
          {
            CSyntax = 0;
            fprintf(p_sreport, "Current Bbv_Buffer_Size is %d. Invalid Bbv_Buffer_Size input.\n",bbv_buffer_size);
          }

          break; 
      }		
    default: 
      {
        CSyntax = 0;
        fprintf(p_sreport, "\n No such level_ID. \n");
      }
    }

    break;
  case 1:
    if(time_code_flag)
    {
      DropFrameFlag    = (time_code && 0x800000)>>23;
      TimeCodeHours    = (time_code && 0x7C0000)>>18;
      TimeCodeMinutes  = (time_code && 0x03F000)>>12;
      TimeCodeSeconds  = (time_code && 0x000FC0)>>6;
      TimeCodePictures = (time_code && 0x00003F);

      if(TimeCodeHours < 0    || TimeCodeHours > 23)
      {
        CSyntax = 0;
        fprintf(p_sreport, "\nInvalid TimeCodeHours value: %d! TimeCodeHours should be in the range 0 ~ 23!\n", TimeCodeHours);
      }
      if(TimeCodeMinutes < 0  || TimeCodeMinutes > 59)
      {
        CSyntax = 0;
        fprintf(p_sreport, "\nInvalid TimeCodeMinutes value: %d! TimeCodeMinutes should be in the range 0 ~ 59!\n", TimeCodeMinutes);
      }
      if(TimeCodeSeconds < 0  || TimeCodeSeconds > 59)
      {
        CSyntax = 0;
        fprintf(p_sreport, "\nInvalid TimeCodeSeconds value: %d! TimeCodeSeconds should be in the range 0 ~ 59!\n", TimeCodeSeconds);
      }
      if(TimeCodePictures < 0 || TimeCodePictures > 59)
      {
        CSyntax = 0;
        fprintf(p_sreport, "\nInvalid TimeCodePictures value: %d! TimeCodePictures should be in the range 0 ~ 59!\n", TimeCodePictures);
      }
    }
    break;
  case 2:
    break;
  case 3:
    if(video_format > 5)
    {
      CSyntax = 0;
      fprintf(p_sreport, "\nvideo_format %d is reserved at the current profile\n", video_format);
    }

    if(color_description)
    {
      if(color_primaries==0)
      {
        CSyntax = 0;
        fprintf(p_sreport, "\ncolour_primaries %d is forbidden at the current profile!\n", color_primaries);
      }
      else if(color_primaries>8)
      {
        CSyntax = 0;
        fprintf(p_sreport, "\ncolour_primaries %d is reserved at the current profile!\n", color_primaries);
      }

      if(transfer_characteristics==0)
      {
        CSyntax = 0;
        fprintf(p_sreport, "\ntransfer_characteristics %d is forbidden at the current profile!\n", transfer_characteristics);
      }
      else if(transfer_characteristics>10)
      {
        CSyntax = 0;
        fprintf(p_sreport, "\ntransfer_characteristics %d is reserved at the current profile!\n", transfer_characteristics);
      }

      if(matrix_coefficients==0)
      {
        CSyntax = 0;
        fprintf(p_sreport, "\nmatrix_coefficients %d is forbidden at the current profile!\n", matrix_coefficients);
      }
      else if(matrix_coefficients>7)
      {
        CSyntax = 0;
        fprintf(p_sreport, "\nmatrix_coefficients %d is reserved at the current profile!\n", matrix_coefficients);
      }
    }
    break;
  case 4:
    break;
  case 5:
    if(vertical_angle_of_view > 1800000)
    {
      CSyntax = 0;
      fprintf(p_sreport, "\nInvalid vertical_angle_of_view :%d! vertical_angle_of_view shoule be in the range 0 ~ 1800000\n", vertical_angle_of_view);
    }
    break;
  case 6:
    break;
  case 7:
    curr_mb_row = img->current_mb_nr / (img->PicWidthInMbs);
    if(vertical_size<2800)
    {
      if(curr_mb_row != slice_vertical_position)
      {
        CSyntax = 0;
        fprintf(p_sreport, "\nInvalid Slice_vertical_position %d! Current mb_row is %d!\n", slice_vertical_position, curr_mb_row);
      }
    }
    else
    {
      if(curr_mb_row != ((slice_vertical_position_extension<<7) + slice_vertical_position))
      {
        CSyntax = 0;
        fprintf(p_sreport, "\nInvalid Slice_vertical_position %d! Current mb_row is %d!\n", 
          ((slice_vertical_position_extension<<7) + slice_vertical_position), curr_mb_row);
      }
    }
    break;
  default:
    break;
  }
}

void CheckBitrate(int bitrate, int second)
{
  switch(level_id) {
  case 0x10:
    if(bitrate > 1000000)
    {
      fprintf(stdout, "The %d second's bitrate: %d. Exceed level %d's maximum bitrate 1000000!", second,bitrate,level_id);
      CSyntax = 0;
    }
    break;
  case 0x11:
    if(bitrate > 2500000)
    {
      fprintf(stdout, "The %d second's bitrate: %d. Exceed level %d's maximum bitrate 2500000!", second,bitrate,level_id);
      CSyntax = 0;
    }
    break;
  case 0x20:
    if(bitrate > 10000000)
    {
      fprintf(stdout, "The %d second's bitrate: %d. Exceed level %d's maximum bitrate 10000000!", second,bitrate,level_id);
      CSyntax = 0;
    }
    break;
  case 0x40:
    if(bitrate > 20000000)
    {
      fprintf(stdout, "The %d second's bitrate: %d. Exceed level %d's maximum bitrate 20000000!", second,bitrate,level_id);
      CSyntax = 0;
    }
    break;
  case 0x41:
    if(bitrate > 50000000)
    {
      fprintf(stdout, "The %d second's bitrate: %d. Exceed level %d's maximum bitrate 50000000!", second,bitrate,level_id);
      CSyntax = 0;
    }
    break;
  default:
    break;
  }
  return;
}
